/**
 * +----------------------------------------------------------------------------------------------+
 * |Date               |  Version  |Author             |Description                              |
 * |==========+=======+==============+===================|
 * |2018年2月6日     |  1.0.0       | kreo                 |Initial                                       |
 * +----------------------------------------------------------------------------------------------+
 */
package main.java.utils.hxy.thread.thread;

import com.wanma.framework.common.service.CommonService;
import com.wanma.framework.config.exception.IException;
import com.wanma.framework.util.INum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * @ClassName IThreadPool
 * @Description
 */
public class IThreadPool {
    private final static Logger log = LoggerFactory.getLogger(IThreadPool.class);
    private final static Integer poolSize = 8;// 初始线程 , 也是最小智能线程
    private final static Integer keepAliveSecond = 15;// 线程空余销毁时间
    private final static Integer maxQueueSize = 3000;// 最大队列等待数量 , 到最大后, 开始增加到maxThread
    // private final static Integer incStep = 30;  //每当queue.size()

    private static Integer maxPoolSize = 128;// 最大智能线程

    //跃迁系数 , 当队列平均等待时间大于1500的时候 , 向上跃迁线程池大小
    private static long transitionMills = 1000L;

    // 设定队列最大100个任务
    private static TimeLinkedBlockingQueue<Runnable> queue = new TimeLinkedBlockingQueue<>(maxQueueSize);
    private static ThreadPoolExecutor executor = null;

    public static ThreadPoolExecutor getPool() {
        try {
            if (executor == null) {
                executor = new ThreadPoolExecutor(
                        poolSize,
                        poolSize,
                        keepAliveSecond,
                        TimeUnit.SECONDS,
                        queue // 超出初始线程后会存放在此处等待处理,如果queue容量不够,才会继续增大线程到最大线程
                        // 如果最大线程也不能处理.则直接抛错
                );
                // 允许核心线程也进行释放
                executor.allowCoreThreadTimeOut(true);
                executor.setRejectedExecutionHandler(new MailRejectedExecutionHandler());
            }
            return executor;
        } finally {
            log.debug(IPool.getStatus(executor));
        }
    }

    /**
     * 线程池智能调节程序 ,根据既定策略进行调整
     */
    public static ThreadPoolExecutor getAgentPool() {
        ThreadPoolExecutor executor = getPool();
        try {
            // 当前的线程池大小
            int coreSize = executor.getCorePoolSize();
            // 当前活跃线程大小
            int activeSize = executor.getActiveCount();

            ///////////////////////////////////////////
            // 得到当前平均等待时间
            long currAvgTime = getQueue().getAvgWaitTime();
            if (currAvgTime > transitionMills) {
                if (coreSize < maxPoolSize) {
                    // 如果已经大于跃迁值 , 并且小于最大线程数 , 则进行跃迁
                    IPool.setSize(executor, coreSize * 2);
                    log.info("调整线程池大小 " + coreSize + " >>> " + coreSize * 2);
                    // 平均时间清0 , 重新开始计算 , 以免干扰后面的结果
                    getQueue().resetAvgWaitTime();
                    // 考虑如何影响效率进行邮件发送
                    // if (coreSize * 2 == maxPoolSize) {
                    //     CommonService.me.sendTextAlarm("zhoujunyu@wanmagroup.com", "连接池报警 Server:" + IUtils.getIp(), "连接池已经达到最大Size >>> ");
                    // }
                } else {
                    log.warn("超过临界值,但是连接池已经到达最大限度[" + maxPoolSize + "],请根据实际情况调整连接池.");
                }
            } else if (coreSize > poolSize && activeSize <= (coreSize / 4) && getQueue().size() == 0) {
                // currAvgTime <= transitionMills &&
                // 如果时间已经降下来了, 线程活动树是coreSize的1/4(跨2级调节幅度)
                // 并且已经无等待 , 则线程也可以减半了
                // 但不能减到最小poolSize
                IPool.setSize(executor, coreSize / 2);
                log.info("调整线程池大小 " + coreSize + " >>> " + coreSize / 2);
                // 平均时间清0 , 重新开始计算 , 以免干扰后面的结果
                getQueue().resetAvgWaitTime();
            }

            return executor;
        } finally {
        }
    }

    /**
     * 执行一个任务
     *
     * @param task
     */
    public static void addTask(Runnable task) {
        getAgentPool().execute(task);
    }

    /**
     * 马上得到Future
     * 2018年2月6日 下午2:11:41
     *
     * @param callable
     * @return
     * @Author kreo
     */
    public static <T> Future<T> addCallable(Callable<T> callable) {
        return getAgentPool().submit(callable);
    }

    public static TimeLinkedBlockingQueue<Runnable> getQueue() {
        return queue;
    }

    /**
     * 设定跃迁系数 , 默认为1500
     *
     * @param transitionMills
     */
    public static long setTransitionMills(long transitionMills) {
        IThreadPool.transitionMills = transitionMills;
        return IThreadPool.transitionMills;
    }

    /**
     * 设置最大线程数 , 请设置成8的倍数
     *
     * @param maxPoolSize
     * @return
     */
    public static Integer setMaxPoolSize(int maxPoolSize) {
        // 如果符合条件
        if (maxPoolSize >= poolSize && INum.is2Power(maxPoolSize)) {
            IThreadPool.maxPoolSize = maxPoolSize;
            return IThreadPool.maxPoolSize;
        } else {
            IException.throwException("参数不符合: 大于" + poolSize + "并且为2的指数次幂");
            return 0;
        }
    }

    public static class MailRejectedExecutionHandler implements RejectedExecutionHandler {
        // 设定备用线程池 , 备用线程池为高上限线程池
        private static ThreadPoolExecutor backupPool = null;

        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                getBackPool().execute(r);
            } finally {
                CommonService.me.sendTextAlarm("zhoujunyu@wanmagroup.com", "连接池报警", "已经开始使用备用连接池");
            }
        }

        public static ThreadPoolExecutor getBackPool() {
            if (backupPool == null) {
                backupPool = new ThreadPoolExecutor(
                        0, Integer.MAX_VALUE,
                        30, TimeUnit.SECONDS,
                        new SynchronousQueue<Runnable>());
                // 允许核心线程也进行释放
                backupPool.allowCoreThreadTimeOut(true);
                // executor.setRejectedExecutionHandler(handler);
            }
            return backupPool;
        }
    }

    public static void shutdown() {
        IPool.shutdown(executor);
        log.info("关闭IThreadPool成功");
    }
}